﻿
using MathNet.Numerics.LinearAlgebra;
using System.Data;
using static Graph.Algorithm.Core.QuantGeneticService;

namespace Graph.Algorithm.Core
{
    public class QuantGeneticService
    {
        #region builder

        private static Random randomBuilder { get; set; } = new Random();
        private static VectorBuilder<double> vectorBuilder { get; set; } = Vector<double>.Build;
        private static MatrixBuilder<double> matBuilder { get; set; } = Matrix<double>.Build;

        #endregion

        #region data

        /// <summary>
        /// 量子群
        /// </summary>
        public static List<Quant[]> sequences { get; set; } = new List<Quant[]>();

        /// <summary>
        /// 目标函数  也就是评价函数
        /// </summary>
        public static Func<Quant[], double> fitnessFunc { get; set; } = (q) => { return 1; };

        /// <summary>
        /// 当前最佳量子
        /// </summary>
        public static Quant[] bestSequence { get; set; }

        public static Dictionary<string, double> thetaDict { get; set; } = new Dictionary<string, double>();

        #endregion

        /// <summary>
        /// 一个序列长度  即一个序列内的量子个数
        /// </summary>
        /// <param name="sequenceLen"></param>
        public QuantGeneticService(int sequenceLen, Func<Quant[], double> _fitnessFunc)
        {
            fitnessFunc = _fitnessFunc;
            //初始化量子自旋角度判断表
            thetaDict = GenThetaStrDict();

            //建立量子群
            for (int i = 0; i < 10; i++)
            {
                List<Quant> sequence = new List<Quant>();
                //Quant[] sequence = new Quant[ ] { };
                for (int j = 0; j < sequenceLen; j++)
                {
                    var quant = new Quant();
                    quant.id = j;
                    sequence.Add(quant);
                }
                sequences.Add(sequence.ToArray());
            }
        }

        public void Run(int numberOfGeneration)
        {

            while (numberOfGeneration > 0)
            {
                numberOfGeneration--;

                //寻最优 
                foreach (var sequence in sequences)
                {
                    sequence.ToList().ForEach(q => q.Collapse());
                }
                sequences = sequences.OrderBy(s => fitnessFunc(s)).ToList();
                bestSequence = sequences.First();

                //开始量子自旋变异
                sequences = sequences.Select(s => QuantRotate(s)).ToList();

            }
        }


        public static Dictionary<int[], double> GenThetaDict()
        {
            var sign = Math.Sign(randomBuilder.NextDouble() - 0.5d);

            Dictionary<int[], double> pairs = new Dictionary<int[], double>();
            pairs.Add(new int[] { 0, 0, 0, 0 }, 0);
            pairs.Add(new int[] { 0, 0, 1, 0 }, 0);

            //xi  besti  fg  ab>0  a=0
            pairs.Add(new int[] { 0, 1, 0, 1 }, +1d);
            pairs.Add(new int[] { 0, 1, 0, 2 }, -1d);
            pairs.Add(new int[] { 0, 1, 0, 3 }, 0d);
            pairs.Add(new int[] { 0, 1, 0, 4 }, -1d * sign);

            pairs.Add(new int[] { 0, 1, 1, 1 }, -1d);
            pairs.Add(new int[] { 0, 1, 1, 2 }, 1d);
            pairs.Add(new int[] { 0, 1, 1, 3 }, 1d * sign);
            pairs.Add(new int[] { 0, 1, 1, 4 }, 0d);

            pairs.Add(new int[] { 1, 0, 0, 1 }, -1d);
            pairs.Add(new int[] { 1, 0, 0, 2 }, 1d);
            pairs.Add(new int[] { 1, 0, 0, 3 }, 1d * sign);
            pairs.Add(new int[] { 1, 0, 0, 4 }, 0d);

            pairs.Add(new int[] { 1, 0, 1, 1 }, 1d);
            pairs.Add(new int[] { 1, 0, 1, 2 }, -1d);
            pairs.Add(new int[] { 1, 0, 1, 3 }, 0d);
            pairs.Add(new int[] { 1, 0, 1, 4 }, 1d * sign);


            pairs.Add(new int[] { 1, 1, 0, 0 }, 0);
            pairs.Add(new int[] { 1, 1, 1, 0 }, 0);

            return pairs;
        }

        public static Dictionary<string, double> GenThetaStrDict( )
        {
            var sign = Math.Sign(randomBuilder.NextDouble() - 0.5d);

            Dictionary<string, double> pairs = new Dictionary<string, double>();
             
            pairs.Add("0000", 0);
            pairs.Add("0010", 0);

            //xi  besti  fg  ab>0  a=0
            pairs.Add("0101", +1d);
            pairs.Add("0102", -1d);
            pairs.Add("0103", 0d);
            pairs.Add("0104", -1d * sign);

            pairs.Add("0111", -1d);
            pairs.Add("0112", 1d);
            pairs.Add("0113", 1d * sign);
            pairs.Add("0114", 0d);

            pairs.Add("1001", -1d);
            pairs.Add("1002", 1d);
            pairs.Add("1003", 1d * sign);
            pairs.Add("1004", 0d);

            pairs.Add("1011", 1d);
            pairs.Add("1012", -1d);
            pairs.Add("1013", 0d);
            pairs.Add("1014", 1d * sign);

            pairs.Add("1100", 0);
            pairs.Add("1110", 0);

             
             
            return pairs;
        }


        /// <summary>
        /// 量子旋转门
        /// </summary>
        /// <param name="sequence"></param>
        /// <returns></returns>
        public static Quant[] QuantRotate(Quant[] sequence)
        {
            //Quant[] quants = new Quant[sequence.Length];
            //sequence.ToList().ForEach(q=>q.Collapse());
            List<Quant> quants = new List<Quant>();

            var thetas = GenTheta(sequence, bestSequence);
            for (int i = 0; i < thetas.Count(); i++)
            {
                var quant = sequence[i];
                var theta = thetas[i] * 0.01 * Math.PI;
                double[,] array2d = {
                    { Math.Cos(theta), -Math.Sin(theta)  },
                    { Math.Sin(theta),  Math.Cos(theta)  }};
                var rotMat = matBuilder.DenseOfArray(array2d);
                var vec = rotMat.Multiply(quant.ToVector());
                quants.Add(new Quant(vec) { id = quant.id });
            }
            return quants.ToArray();
        }


        public static double[] GenTheta(Quant[] currQuant, Quant[] bestQuant)
        {
            List<double> thetas = new List<double>();

            for (int i = 0; i < currQuant.Length; i++)
            {
                var currQ = currQuant[i];
                var bestQ = bestQuant[i];

                int xi = currQ.collapseValue;
                int bi = bestQ.collapseValue;
                int fxfb = fitnessFunc(currQuant) - fitnessFunc(bestQuant) > 0 ? 1 : 0;

                int sign = 0;
                sign = currQ.alpha * currQ.beta > 0d ? 1 : 0;
                sign = currQ.alpha * currQ.beta < 0d ? 2 : 0;
                sign = currQ.alpha == 0 ? 3 : 0;
                sign = currQ.beta == 0 ? 4 : 0;

                var arr = new int[4];
                arr.Append(xi);
                arr.Append(bi);
                arr.Append(fxfb);
                arr.Append(sign);

                string arrStr = string.Join("",arr);
                //var theta = thetaDict.Where(d => d.Key == arrStr).First().Value;
                thetas.Add(thetaDict[arrStr]);
            }
            return thetas.ToArray();
        }

        public static int Comment(int[] ints)
        {
            // Convert.ToInt32("1010", 2);//输出: 10
            string bb = string.Join("", ints);
            int res = Convert.ToInt32(bb, 2);
            return res;
        }


        /// <summary>
        /// 
        /// </summary>
        public class Quant
        {
            public Quant()
            {
                this.id = 0;
                this.alpha = 1d / Math.Sqrt(2);
                this.beta = GetBeta();
            }

            public Quant(Vector<double> vec)
            {
                this.id = 0;
                this.alpha = vec[0];
                this.beta = vec[1];
            }

            /// <summary>
            /// 量子测量塌缩
            /// </summary>
            /// <returns></returns>
            public int Collapse()
            {
                var tag = randomBuilder.NextDouble();
                var res = Math.Pow(this.alpha, 2) > tag ? 0 : 1;
                this.collapseValue = res;
                return res;
            }

            public double GetAlpha()
            {
                return Math.Sqrt(1 - this.beta * this.beta);
            }
            public double GetBeta()
            {
                return Math.Sqrt(1 - this.alpha * this.alpha);
            }

            public Vector<double> ToVector()
            {
                var vec = vectorBuilder.Dense(2);
                vec[0] = this.alpha;
                vec[1] = this.beta;
                return vec;
            }

            public int id { get; set; }
            public double alpha { get; set; }
            public double beta { get; set; }

            public int collapseValue { get; set; }
        }


    }




}
